﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Caching;
using System.Text;
//using Microsoft.Isam.Esent.Collections.Generic;
using QstkCore.Domain;

namespace QstkCore.DataAccess
{
    public class DataAccess
    {
        private readonly string SOURCE_DATA_PATH;
        //private readonly string CACHE_DATA_PATH;
        private readonly ObjectCache cache;
        //private readonly PersistentDictionary<string, string> diskCache;

        private static DataAccess dataAccess;

        private DataAccess()
        {
            SOURCE_DATA_PATH = Environment.GetEnvironmentVariable("QSDATA");
            if (SOURCE_DATA_PATH == null)
            {
                Console.WriteLine("Have you set up the QSDATA environment variable ?");
                throw new Exception("Have you set up the QSDATA environment variable ?");
            }
            SOURCE_DATA_PATH += "/Yahoo/";

            //CACHE_DATA_PATH = Environment.GetEnvironmentVariable("TEMP");

            cache = MemoryCache.Default;
            //diskCache = new PersistentDictionary<string, string>(CACHE_DATA_PATH);
        }

        public static DataAccess GetInstance()
        {
            if (dataAccess == null)
                dataAccess = new DataAccess();

            return dataAccess;
        }

        private void CacheData(string key, MarketPrices data)
        {
            var policy = new CacheItemPolicy();
            cache.Set(key, data, policy);

            //var stringWriter = new StringWriter();
            //data.WriteXml(stringWriter);
            //diskCache[key] = stringWriter.ToString();
        }

        private MarketPrices ReadCache(string key)
        {
            var data = cache[key] as MarketPrices;
            return data;

            // TODO: Do we really need a disk cache when it takes 2 seconds to read all the CSVs?
            //// If not in memory try disk
            //if (data != null)
            //    return data;
            //if (diskCache.ContainsKey(key))
            //{
            //    string xmlData = diskCache[key];
            //    return MarketPrices.FromXml(xmlData);
            //}
            //return null;
        }

        /// <summary>
        /// Read data into a DataFrame, but check to see if it is in a cache first.
        /// @param TimeSeries: List of TimeSeries for which the data values are needed. TimeSeries must be sorted.
        /// @param symbols: The list of symbols for which the data values are needed
        /// @param dataItem: The dataItem needed. Like open, close, volume etc.  May be a list, in which case a list of DataFrame is returned.
        /// @param bIncDelist: If true, delisted securities will be included.
        /// @note: If a symbol is not found then a message is printed. All the values in the column for that stock will be NaN. Execution then 
        /// continues as usual. No errors are raised at the moment.
        /// </summary>
        public MarketPrices GetMarketData(IEnumerable<string> symbols, string dataItem, TimeSeries timeSeries)
        {
            string key = GetCacheKey(timeSeries, symbols, dataItem);

            MarketPrices data = ReadCache(key);
            if (data != null) return data;
            data = MarketPrices.ReadData(symbols, dataItem, timeSeries);
            CacheData(key, data);
            return data;
        }
         
        private string GetCacheKey(TimeSeries tsList, IEnumerable<string> symbolList, string dataItem)
        {
            // Create the hash for the symbols
            int hashsyms = symbolList.Select(sym => sym.GetHashCode()).Aggregate(0, (current, hash) => (current + hash)%10000000);

            // Create the hash for the TimeSeries
            int hashts = tsList.Start.GetHashCode() + tsList.Count;

            string hashstr = "qstk-" + "-" + Math.Abs(hashsyms) + "-" + Math.Abs(hashts) + "-" + dataItem.GetHashCode();

            return hashstr;
        }

        public string GetFilePath(string symbol)
        {
            var sb = new StringBuilder();
            sb.Append(SOURCE_DATA_PATH).Append(symbol).Append(".csv");
            return sb.ToString();
        }
    }
}
